#pragma once
#pragma message "HI3863 Hardware SPI support added"
#include "gpio.h"
#include "pinctrl.h"
#include "iot_gpio.h"
#include "hal_gpio.h"
#include "platform_core_rom.h"
#include "soc_osal.h"
#include "spi.h"
#include "platform_core.h"

FASTLED_NAMESPACE_BEGIN

#pragma message "Targeting HI3863S3, which has better SPI support. Configuring for flexible pin assignment."

// 定义LED的最大数量，这里设置为254
#define MAX_LED_NUMBERS 254
// 定义数据的最大长度，根据LED数量和每个LED数据长度计算
#define MAX_DATA_LEN (MAX_LED_NUMBERS * 4) + 8//以sk9822为模版

#define SPI_SLAVE_NUM                   1
#define SPI_FREQUENCY                   1
#define SPI_CLK_POLARITY                0
#define SPI_CLK_PHASE                   0
#define SPI_FRAME_FORMAT                0
#define SPI_FRAME_FORMAT_STANDARD       0
#define SPI_FRAME_SIZE_8                0x1f
#define SPI_TMOD                        0
#define SPI_WAIT_CYCLES                 0x10

#define SPI_TASK_DURATION_MS            500
#define SPI_TASK_PRIO                   24
#define SPI_TASK_STACK_SIZE             0x4000

#define SPI_CLK_FREQ    7*1000000


template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, uint32_t SPI_SPEED>
class HI3863SPIOutput {
	Selectable 	*m_pSelect;

	static inline uint8_t led_strip_datas[MAX_DATA_LEN];
	static inline uint8_t led_strip_data_len = 0;

	#if defined(CONFIG_SPI_SUPPORT_INTERRUPT) && (CONFIG_SPI_SUPPORT_INTERRUPT == 1)
	static inline void fastspi_spi_master_write_int_handler(const void *buffer, uint32_t length)
	{
		unused(buffer);
		unused(length);
		////osal_printk("fastspi_spi_master_write_int_handler interrupt start!\r\n");
	}

	static inline void fastspi_spi_master_rx_callback(const void *buffer, uint32_t length, bool error)
	{
		if (buffer == NULL || length == 0) {
			osal_printk("fastspi_spi_master_rx_callback spi master transfer illegal data!\r\n");
			return;
		}
		if (error) {
			osal_printk("fastspi_spi_master_rx_callback error!\r\n");
			return;
		}

		uint8_t *buff = (uint8_t *)buffer;
		for (uint32_t i = 0; i < length; i++) {
			osal_printk("buff[%d] = %x\r\n", i, buff[i]);
		}
		//osal_printk("fastspi_spi_master_rx_callback success!\r\n");
	}
	#endif  /* CONFIG_SPI_SUPPORT_INTERRUPT */

	static inline void fastspi_spi_init_pin(void)
	{
		//osal_printk("fastspi_spi_init_pin entry!\r\n");
		uapi_pin_set_mode((pin_t)CONFIG_SPI_DI_MASTER_PIN, (pin_mode_t)CONFIG_SPI_MASTER_PIN_MODE);
		uapi_pin_set_mode((pin_t)CONFIG_SPI_DO_MASTER_PIN, (pin_mode_t)CONFIG_SPI_MASTER_PIN_MODE);
		uapi_pin_set_mode((pin_t)CONFIG_SPI_CLK_MASTER_PIN,(pin_mode_t)CONFIG_SPI_MASTER_PIN_MODE);
		uapi_pin_set_mode((pin_t)CONFIG_SPI_CS_MASTER_PIN, (pin_mode_t)CONFIG_SPI_MASTER_PIN_MODE);
	}

	static inline void fastspi_spi_master_init_config(void)
	{
		//osal_printk("fastspi_spi_master_init_config entry!\r\n");
		spi_attr_t config = { 0 };
		spi_extra_attr_t ext_config = { 0 };

		config.is_slave = false;
		config.slave_num = SPI_SLAVE_NUM;
		config.bus_clk = SPI_CLK_FREQ;
		config.freq_mhz = SPI_FREQUENCY;
		config.clk_polarity = SPI_CLK_POLARITY;
		config.clk_phase = SPI_CLK_PHASE;
		config.frame_format = SPI_FRAME_FORMAT;
		config.spi_frame_format = HAL_SPI_FRAME_FORMAT_STANDARD;
		config.frame_size = SPI_FRAME_SIZE_8;
		config.tmod = SPI_TMOD;
		config.sste = 1;

		ext_config.qspi_param.wait_cycles = SPI_WAIT_CYCLES;

	#if defined(CONFIG_SPI_SUPPORT_DMA) && (CONFIG_SPI_SUPPORT_DMA == 1)
		uapi_dma_init();
		uapi_dma_open();
	#endif  /* CONFIG_SPI_SUPPORT_DMA */

		uapi_spi_init((spi_bus_t)CONFIG_SPI_MASTER_BUS_ID, &config, &ext_config);

	#if defined(CONFIG_SPI_SUPPORT_INTERRUPT) && (CONFIG_SPI_SUPPORT_INTERRUPT == 1)
		if (uapi_spi_set_irq_mode((spi_bus_t)CONFIG_SPI_MASTER_BUS_ID, true, fastspi_spi_master_rx_callback,
			fastspi_spi_master_write_int_handler) == ERRCODE_SUCC) {
			//osal_printk("spi%d master set irq mode succ!\r\n", CONFIG_SPI_MASTER_BUS_ID);
		}
	#endif  /* CONFIG_SPI_SUPPORT_INTERRUPT */
		memset(led_strip_datas, 0, sizeof(led_strip_datas));
		led_strip_data_len = 0;
	}

	static inline void hi3863_transfer_byte(uint8_t b){
		uint8_t rx_data[1] = { 0 };
		uint8_t tx_data[1] = { b };
		spi_xfer_data_t data = {
			.tx_buff = tx_data,
			.tx_bytes = 1,
			.rx_buff = rx_data,
			.rx_bytes = 1,
		};
		if (uapi_spi_master_write((spi_bus_t)CONFIG_SPI_MASTER_BUS_ID, &data, 0xFFFFFFFF) == ERRCODE_SUCC) {
			//osal_printk("spi%d hi3863_transfer_byte send succ!\r\n", CONFIG_SPI_MASTER_BUS_ID);
		}else{
			osal_printk("spi%d hi3863_transfer_byte send fail!\r\n", CONFIG_SPI_MASTER_BUS_ID);
		}
	}

	static inline void hi3863_transfer_bytes(uint8_t *pdata,int len){
		uint8_t rx_data[1] = { 0 };
		spi_xfer_data_t data = {
			.tx_buff = pdata,
			.tx_bytes = len,
			.rx_buff = rx_data,
			.rx_bytes = 1,
		};
		if (uapi_spi_master_write((spi_bus_t)CONFIG_SPI_MASTER_BUS_ID, &data, 0xFFFFFFFF) == ERRCODE_SUCC) {
			osal_printk("spi%d hi3863_transfer_bytes send succ!\r\n", CONFIG_SPI_MASTER_BUS_ID);
		}
	}

public:
	HI3863SPIOutput() { m_pSelect = NULL; }
	HI3863SPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
	void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }

	void init() {
		// set the pins to output and make sure the select is released (which apparently means hi?  This is a bit
		// confusing to me)
		/* SPI pinmux. */
		fastspi_spi_init_pin();

		/* SPI master init config. */
		fastspi_spi_master_init_config();
	}

	// naive writeByte implelentation, simply calls writeBit on the 8 bits in the byte.
	static void writeByte(uint8_t b) {
		led_strip_datas[led_strip_data_len] = b;
		//osal_printk("spi%d writeByte  succ led_strip_data_len=%d data=0x%x!\r\n",CONFIG_SPI_MASTER_BUS_ID,led_strip_data_len,b);
		led_strip_data_len = led_strip_data_len+1;
	}

	// stop the SPI output.  Pretty much a NOP with software, as there's no registers to kick
	static void stop() { }

	// wait until the SPI subsystem is ready for more data to write.  A NOP when bitbanging
	static void wait() __attribute__((always_inline)) { }
	static void waitFully() __attribute__((always_inline)) { 
		wait(); 
		if(led_strip_data_len > 0 ) {
			hi3863_transfer_bytes(led_strip_datas, led_strip_data_len);
			//osal_printk("spi%d waitFully send succ led_strip_data_len=%d!\r\n", CONFIG_SPI_MASTER_BUS_ID,led_strip_data_len);
		}
	}

	static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); }
	static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); wait(); }

	static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); }

public:

	// select the SPI output (TODO: research whether this really means hi or lo.  Alt TODO: move select responsibility out of the SPI classes
	// entirely, make it up to the caller to remember to lock/select the line?)
	void select() { 
		if(m_pSelect != NULL) { m_pSelect->select(); } 
	} 

	// release the SPI line
	void release() { 
		if(m_pSelect != NULL) { m_pSelect->release(); } 
		led_strip_data_len = 0;
		memset(led_strip_datas, 0, sizeof(led_strip_datas));
	}

	// Write out len bytes of the given value out over m_ledSPI.  Useful for quickly flushing, say, a line of 0's down the line.
	static void writeBytesValue(uint8_t value, int len) {
		select();
		hi3863_transfer_bytes(&value, len);
		release();
	}

	static void writeBytesValueRaw(uint8_t value, int len) {
		hi3863_transfer_bytes(&value, len);
	}

	// write a block of len uint8_ts out.  Need to type this better so that explicit casts into the call aren't required.
	// note that this template version takes a class parameter for a per-byte modifier to the data.
	template <class D> void writeBytes(FASTLED_REGISTER uint8_t *data, int len) {
		select();
		uint8_t *end = data + len;
		while(data != end) {
			writeByte(D::adjust(*data++));
		}
		D::postBlock(len);
		release();
	}

	// default version of writing a block of data out to the SPI port, with no data modifications being made
	void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }

	// write a single bit out, which bit from the passed in byte is determined by template parameter
	template <uint8_t BIT> inline void writeBit(uint8_t b) {
		//osal_printk("spi%d writeBit send succ!\r\n", CONFIG_SPI_MASTER_BUS_ID);
		hi3863_transfer_byte(b);
	}

	// write a block of uint8_ts out in groups of three.  len is the total number of uint8_ts to write out.  The template
	// parameters indicate how many uint8_ts to skip at the beginning of each grouping, as well as a class specifying a per
	// byte of data modification to be made.  (See DATA_NOP above)
	template <uint8_t FLAGS, class D, EOrder RGB_ORDER>  __attribute__((noinline)) void writePixels(PixelController<RGB_ORDER> pixels) {
		select();
		int len = pixels.mLen;
		//osal_printk("spi%d writePixels send succ!\r\n", CONFIG_SPI_MASTER_BUS_ID);
		while(pixels.has(1)) {
			if(FLAGS & FLAG_START_BIT) {
				writeBit<0>(1);
			}
			writeByte(D::adjust(pixels.loadAndScale0()));
			writeByte(D::adjust(pixels.loadAndScale1()));
			writeByte(D::adjust(pixels.loadAndScale2()));
			pixels.advanceData();
			pixels.stepDithering();
		}
		D::postBlock(len);
		release();
	}
};

FASTLED_NAMESPACE_END
